#define OGRE_STATIC_GLES
#define OGRE_STATIC_ParticleFX
#define OGRE_STATIC_OctreeSceneManager
#define OGRE_STATIC_UltimatePhysX3Plugin
#include "OgreStaticPluginLoader.h"

#include "Ogre.h"
#include "OgreLog.h"
#include "OgreRenderWindow.h"
#include "OgreStringConverter.h"
#include "Android/OgreAndroidEGLWindow.h"
#include "Android/OgreAPKFileSystemArchive.h"
#include "Android/OgreAPKZipArchive.h"
#include "UltimateSubsystemRegistry.h"
#include "Game.h"
#include "AndroidScriptFileReader.h"
#include "UltimateInputSubsystem.h"
#include "UltimateAndroidInputSubsystem.h"
#include "UltimateAndroidGameOverHandler.h"
#include "UltimateAsioNetworkSubsystem.h"
#include "UltimatePhysX3Subsystem.h"

#include <EGL/egl.h>
#include <android/log.h>
#include <android_native_app_glue.h>
#include <android/asset_manager.h>
#include <android/window.h>
#include <bits/c++config.h>

/*
 extern "C" {
 #include "lua.h"
 #include "lualib.h"
 #include "lauxlib.h"
 }*/

// This is the only header we need to include for LuaBind to work
#include "luabind/luabind.hpp"
#include "luabind/lua_include.hpp"

#include <stdio.h>
#include <iostream>
#include <sstream>
#include <iterator>
#include <algorithm>

static Ogre::RenderWindow* gRenderWnd = NULL;
static Ogre::Root* gRoot = NULL;
static Ogre::StaticPluginLoader* gStaticPluginLoader = NULL;
static AAssetManager* gAssetMgr = NULL;
static Ogre::SceneManager* gSceneMgr = NULL;
static UltimateAndroidInputSubsystem * inputSubsystem = NULL;

struct saved_state {
	float x0;
	float y0;
	float x1;
	float y1;
};

static Ogre::DataStreamPtr openAPKFile(const Ogre::String& fileName) {
	Ogre::DataStreamPtr stream;
	AAsset* asset = AAssetManager_open(gAssetMgr, fileName.c_str(),
			AASSET_MODE_BUFFER);
	if (asset) {
		off_t length = AAsset_getLength(asset);
		void* membuf = OGRE_MALLOC(length, Ogre::MEMCATEGORY_GENERAL);
		memcpy(membuf, AAsset_getBuffer(asset), length);
		AAsset_close(asset);

		stream = Ogre::DataStreamPtr(
				new Ogre::MemoryDataStream(membuf, length, true, true));
	}
	return stream;
}

static void loadResources() {
	Ogre::ConfigFile cf;
	cf.load(openAPKFile("resources.cfg"));

	Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
	while (seci.hasMoreElements()) {
		Ogre::String sec, type, arch;
		sec = seci.peekNextKey();
		Ogre::ConfigFile::SettingsMultiMap* settings = seci.getNext();
		Ogre::ConfigFile::SettingsMultiMap::iterator i;

		for (i = settings->begin(); i != settings->end(); i++) {
			type = i->first;
			arch = i->second;
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation(arch,
					type, sec);
		}
	}

	Ogre::ResourceGroupManager::getSingletonPtr()->initialiseResourceGroup(
			Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

	/*gSceneMgr = gRoot->createSceneManager(Ogre::ST_GENERIC);

	 Ogre::SceneNode* camNode =
	 gSceneMgr->getRootSceneNode()->createChildSceneNode(
	 "CameraSceneNode");
	 Ogre::Camera* camera = gSceneMgr->createCamera("MyCam");
	 camNode->attachObject(camera);

	 gSceneMgr->setSkyBox(true, "purple_nebula_skybox");

	 // Ogre::Entity* pEntity = gSceneMgr->createEntity("SinbadInstance", "Sinbad.mesh");
	 Ogre::Entity* pEntity = gSceneMgr->createEntity("ZealotCorruptedInstance",
	 "Robot.mesh");

	 Ogre::SceneNode* pNode =
	 gSceneMgr->getRootSceneNode()->createChildSceneNode();
	 pNode->attachObject(pEntity);
	 pNode->yaw(Ogre::Radian(Ogre::Degree(180)));

	 Ogre::Light* pDirLight = gSceneMgr->createLight();
	 pDirLight->setDirection(Ogre::Vector3(0, -1, 0));
	 pDirLight->setType(Ogre::Light::LT_DIRECTIONAL);
	 pNode->attachObject(pDirLight);

	 camera->setNearClipDistance(1.0f);
	 camera->setFarClipDistance(100000.0f);
	 camera->setPosition(0, 0, 20.0f);
	 camera->lookAt(0, 0, 0);
	 camera->setAutoAspectRatio(true);

	 Ogre::Viewport* vp = gRenderWnd->addViewport(camera);
	 vp->setBackgroundColour(Ogre::ColourValue(1, 1, 1));*/
}

static int32_t handleInput(struct android_app* app, AInputEvent* event) {

	if (AKeyEvent_getKeyCode(event) == AKEYCODE_BACK) {
		exit(0);
		return 1; // <-- prevent default handler
	};

	/*UltimateAndroidInputSubsystem* inputSubsystem =
			((UltimateAndroidInputSubsystem*) UltimateSubsystemRegistry::getSingletonPtr()->getSubsystem(
					INPUT_SUBSYSTEM));*/
	inputSubsystem->updateInput(event);

	struct saved_state* mState = (struct saved_state*) app->userData;
	if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {

	}

	if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {

		unsigned int flags = AMotionEvent_getAction(event)
				& AMOTION_EVENT_ACTION_MASK;

		if (flags == AMOTION_EVENT_ACTION_DOWN
				|| flags == AMOTION_EVENT_ACTION_POINTER_DOWN) {
			mState->x0 = AMotionEvent_getX(event, 0);
			mState->y0 = AMotionEvent_getY(event, 0);
		} else if (flags == AMOTION_EVENT_ACTION_UP
				|| flags == AMOTION_EVENT_ACTION_POINTER_UP) {

		} else if (flags == AMOTION_EVENT_ACTION_MOVE) {
			mState->x1 = AMotionEvent_getX(event, 0);
			mState->y1 = AMotionEvent_getY(event, 0);

			if (gSceneMgr != NULL) {
				Ogre::Entity* pEntity = gSceneMgr->getEntity(
						"ZealotCorruptedInstance");
				Ogre::SceneNode* pNode = pEntity->getParentSceneNode();

				//pNode->yaw(Ogre::Radian(Ogre::Degree((mState->x1 - mState->x0) / 2)), Ogre::Node::TS_WORLD);
				//pNode->pitch(Ogre::Radian(Ogre::Degree((mState->y1 - mState->y0) / 2)), Ogre::Node::TS_WORLD);

				//Ogre::Camera* camera = gSceneMgr->getCamera("MyCam");
				/*Ogre::SceneNode* camNode = gSceneMgr->getSceneNode("CameraSceneNode");

				 Ogre::Camera* mainCam =   gSceneMgr->getCamera("mainCam");

				 camNode = mainCam->getParentSceneNode();

				 camNode->rotate( Ogre::Vector3::UNIT_Y, Ogre::Radian(Ogre::Degree(-(mState->x1 - mState->x0) / 3)), pNode->TS_LOCAL);
				 camNode->rotate( Ogre::Vector3::UNIT_X, Ogre::Radian(Ogre::Degree(-(mState->y1 - mState->y0) / 3)), pNode->TS_LOCAL);
				 */

				mState->x0 = mState->x1;
				mState->y0 = mState->y1;
			}

		} else if (flags == AMOTION_EVENT_ACTION_CANCEL) {
			return 0;
		} else {
			return 0;
		}
	}
	return 0;
}

void loadingScreen() {

	Ogre::ResourceGroupManager::getSingletonPtr()->createResourceGroup("loading");
		Ogre::ResourceGroupManager::getSingletonPtr()->addResourceLocation("textures/loading", "APKFileSystem", "loading");
		Ogre::ResourceGroupManager::getSingletonPtr()->addResourceLocation("materials/loading","APKFileSystem","loading");
		Ogre::ResourceGroupManager::getSingleton().addResourceLocation("packs/loading_mesh.zip", "APKZip", "loading");
		Ogre::ResourceGroupManager::getSingletonPtr()->initialiseResourceGroup("loading");

		Ogre::SceneManager * gSceneMgr = gRoot->createSceneManager(Ogre::ST_GENERIC);

		 Ogre::SceneNode* camNode =
		 gSceneMgr->getRootSceneNode()->createChildSceneNode(
		 "CameraSceneNode");
		 Ogre::Camera* camera = gSceneMgr->createCamera("MyCam");
		 camNode->attachObject(camera);

		 Ogre::Plane plane;
		 plane.d = 1000;
		 plane.normal = -1*Ogre::Vector3::NEGATIVE_UNIT_Z;

		 gSceneMgr->setSkyPlane(true,plane, "SkyMaterial/SpaceSkyPlane",1500,75);

		 // Ogre::Entity* pEntity = gSceneMgr->createEntity("SinbadInstance", "Sinbad.mesh");
		 Ogre::Entity* pEntity = gSceneMgr->createEntity("ZealotCorruptedInstance",
		 "loading_mesh.mesh");
		 //pEntity->setMaterialName("loading_screen");

		 Ogre::SceneNode* pNode =  gSceneMgr->getRootSceneNode()->createChildSceneNode();
		 pNode->attachObject(pEntity);
		 pNode->yaw(Ogre::Radian(Ogre::Degree(90)));
		 pNode->pitch(Ogre::Radian(Ogre::Degree(90)));
		pNode->roll(Ogre::Radian(Ogre::Degree(180)));
		 pNode->setPosition(-6,0,-6);




		 Ogre::Light* pDirLight = gSceneMgr->createLight();
		 pDirLight->setDirection(Ogre::Vector3(-0.3, -1, 0));
		 pDirLight->setType(Ogre::Light::LT_DIRECTIONAL);
		 pNode->attachObject(pDirLight);

		 gSceneMgr->setAmbientLight(Ogre::ColourValue(0.3,0.3,0.3));


		 camera->setNearClipDistance(1.0f);
		 camera->setFarClipDistance(100000.0f);
		 camera->setPosition(0, 0, 10.0f);
		 camera->lookAt(0, 0, 0);
		 camera->setAutoAspectRatio(true);

		Ogre::Viewport* vp = gRenderWnd->addViewport(camera);
		vp->setBackgroundColour(Ogre::ColourValue(1, 1, 1));

		gRoot->renderOneFrame();
		gRenderWnd->removeViewport(vp->getZOrder());
		gRoot->destroySceneManager(gSceneMgr);
}

static void handleCmd(struct android_app* app, int32_t cmd) {
	struct saved_state* mState = (struct saved_state*) app->userData;
	switch (cmd) {
	case APP_CMD_SAVE_STATE:
		// state?
		break;
	case APP_CMD_INIT_WINDOW:
		if (app->window && gRoot) {
			AConfiguration* config = AConfiguration_new();
			AConfiguration_fromAssetManager(config,
					app->activity->assetManager);
			gAssetMgr = app->activity->assetManager;

			if (!gRenderWnd) {
				Ogre::ArchiveManager::getSingleton().addArchiveFactory(
						new Ogre::APKFileSystemArchiveFactory(
								app->activity->assetManager));
				Ogre::ArchiveManager::getSingleton().addArchiveFactory(
						new Ogre::APKZipArchiveFactory(
								app->activity->assetManager));

				Ogre::NameValuePairList opt;
				opt["externalWindowHandle"] = Ogre::StringConverter::toString(
						(int) app->window);
				opt["androidConfig"] = Ogre::StringConverter::toString(
						(int) config);

				gRenderWnd = gRoot->createRenderWindow("OgreWindow", 0, 0,
						false, &opt);

				/*AAsset* asset = AAssetManager_open(app->activity->assetManager, (const char *) "script/init.lua", AASSET_MODE_UNKNOWN);
				 if (NULL == asset) {
				 //__android_log_print(ANDROID_LOG_ERROR, NF_LOG_TAG, "_ASSET_NOT_FOUND_");
				 Ogre::LogManager::getSingleton().logMessage(Ogre::LML_CRITICAL,"cannot open asset");
				 //return JNI_FALSE;
				 }
				 long size = AAsset_getLength(asset);
				 char* buffer = (char*) malloc (sizeof(char)*size);
				 AAsset_read (asset,buffer,size);
				 //__android_log_print(ANDROID_LOG_ERROR, NF_LOG_TAG, buffer);

				 Ogre::LogManager::getSingleton().logMessage(Ogre::LML_CRITICAL,buffer);
				 AAsset_close(asset);*/


				//Ogre::ResourceGroupManager::getSingletonPtr()->create



				loadingScreen();

				Ogre::LogManager::getSingleton().logMessage(Ogre::LML_CRITICAL, "Loading resources");

				loadResources();

				Ogre::LogManager::getSingleton().logMessage(Ogre::LML_CRITICAL, "Resources loaded");


				AndroidScriptFileReader * scriptFileReader =
						new AndroidScriptFileReader(
								app->activity->assetManager);

				Game *mGame = new Game(gRoot, gRenderWnd);
				mGame->setScriptFileReader(scriptFileReader);
				mGame->setGameOverHandler(
						new UltimateAndroidGameOverHandler(app->activity));
				mGame->initGame();
				gRoot->addFrameListener(mGame);

				/*Ogre::Camera* mainCam =   gSceneMgr->getCamera("mainCam");
				 mainCam->setNearClipDistance(1.0f);
				 mainCam->setFarClipDistance(100000.0f);
				 mainCam->setPosition(0, 0, 20.0f);
				 mainCam->lookAt(0, 0, 0);
				 mainCam->setAutoAspectRatio(true);*/

			} else {
				static_cast<Ogre::AndroidEGLWindow*>(gRenderWnd)->_createInternalResources(
						app->window, config);
			}
			AConfiguration_delete(config);
		}
		break;
	case APP_CMD_TERM_WINDOW:
		if (gRoot && gRenderWnd)
			static_cast<Ogre::AndroidEGLWindow*>(gRenderWnd)->_destroyInternalResources();
		break;
	case APP_CMD_GAINED_FOCUS:
		inputSubsystem->registerSensors();

		break;
	case APP_CMD_LOST_FOCUS:
		inputSubsystem->unRegisterSernsors();

		break;
	case APP_CMD_LOW_MEMORY:

		Ogre::LogManager::getSingleton().logMessage(Ogre::LML_CRITICAL, "LOW MEMORY SITUATION!");
		break;
	}
}

void print_hello(int number) {
	Ogre::LogManager::getSingleton().logMessage(Ogre::LML_CRITICAL, "LUA");
}

void android_main(struct android_app* state) {
	struct saved_state mState;

	app_dummy();

	if (gRoot == NULL) {
		gRoot = new Ogre::Root();

#ifdef OGRE_STATIC_LIB
		gStaticPluginLoader = new Ogre::StaticPluginLoader();
		gStaticPluginLoader->load();
#endif
		gRoot->setRenderSystem(gRoot->getAvailableRenderers().at(0));
		gRoot->initialise(false);
	}



	/*JNIEnv *env2;

	 state->activity->vm->AttachCurrentThread(&env2, NULL);

	 Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL,
	 "JNI thread attached");

	 __android_log_write(ANDROID_LOG_INFO,"JNITEST","JNI thread attached");

	 jclass gameHandlerClass = env2->FindClass("com/mud/ultimate/GameHandler");


	 if (gameHandlerClass == 0) {

	 Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL,
	 "com/mud/ultimate/GameHandler is empty");

	 __android_log_write(ANDROID_LOG_INFO,"JNITEST","com/mud/ultimate/GameHandler is empty");

	 }else
	 {
	 __android_log_write(ANDROID_LOG_INFO,"JNITEST","com/mud/ultimate/GameHandler is received");


	 }


	 gameHandlerClass = env2->FindClass("android/app/NativeActivity");

	 if (gameHandlerClass == 0) {

	 Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL,
	 "android/app/NativeActivity is empty");

	 __android_log_write(ANDROID_LOG_INFO, "JNITEST",
	 "android/app/NativeActivity is empty");

	 } else {
	 __android_log_write(ANDROID_LOG_INFO, "JNITEST",
	 "android/app/NativeActivity is received");

	 }*/

	state->userData = &mState;
	state->onAppCmd = &handleCmd;
	state->onInputEvent = &handleInput;

	ANativeActivity_setWindowFlags(state->activity, AWINDOW_FLAG_FULLSCREEN|AWINDOW_FLAG_KEEP_SCREEN_ON , 0 );


	int ident, events;
	struct android_poll_source* source;

	Ogre::LogManager::getSingleton().logMessage(Ogre::LML_CRITICAL, "OgreLog");

	new UltimateSubsystemRegistry();

	inputSubsystem = new UltimateAndroidInputSubsystem(state);
	UltimateSubsystemRegistry::getSingletonPtr()->addSubsystem(inputSubsystem);

	UltimateSubsystemRegistry::getSingletonPtr()->addSubsystem(
				new UltimateAsioNetworkSubsystem());

	UltimateSubsystemRegistry::getSingletonPtr()->addSubsystem(
					new UltimatePhysX3Subsystem());


	while (true) {
		while ((ident = ALooper_pollAll(0, NULL, &events, (void**) &source))
				>= 0) {
			if (source != NULL)
				source->process(state, source);

			if (ident == LOOPER_ID_USER) {
				inputSubsystem->handleSensors();
			}

			if (state->destroyRequested != 0)
				return;
		}

		if (gRenderWnd != NULL && gRenderWnd->isActive()) {
			gRenderWnd->windowMovedOrResized();
			gRoot->renderOneFrame();
		}
	}

}

